package io.mft.api.domain;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

import cn.hutool.core.util.ObjectUtil;
import io.mft.commons.exception.MFTException;
import io.mft.commons.util.Unique;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
//@Builder
public class Api implements Unique,Serializable{
	 
	        /**
	 * @说明：TODO
	 * @字段名称 serialVersionUID
	        */  
	    
	private static final long serialVersionUID = -4056372260593207150L;
	private ApiFunctionType apiFunctionType;
	private String key;
	private String name;
	private Module module;
	private List<ApiRegulation> apiRegulations;
	private Input input;
	private Output output;
	private String appKey;
	

	/**
	 * HTTP 路径 和KEY对等
	 */
	@JsonInclude(Include.NON_EMPTY) 
	private String path;
	/**
	 * API元数据路径
	 */
	@JsonIgnore
	//private String metadataPath;
	@JsonInclude(Include.NON_EMPTY) 
	private String requestMethod;
	@JsonIgnore
	private String cacheRegionKey;
	public ApiRegulation lastSyncApiRegulation() {
		ApiRegulation apiRegulation=null;
		for (int i =apiRegulations.size()-3; i >=0; i--) {
			ApiRegulation t=apiRegulations.get(i);
			if (!(t.getRegulation().getExecuteMode()==ExecuteMode.async)) {
				apiRegulation=t;
				break;
			}
		}
		return apiRegulation;
	}
	
	@Override
	public String uniqueId() {
		String k=apiFunctionType.name()+"-"+key+"-"+module.uniqueId()+"-"+input.uniqueId()+"-"+output.uniqueId();
		for (ApiRegulation apiRegulation : apiRegulations) {
			k=k+"-"+apiRegulation.uniqueId();
		}
		return k;
	}
	
	public void init() {
		if (getApiFunctionType()==ApiFunctionType.processExecutor) {
			Regulation inputParameterProcessRegulation=new Regulation();
			inputParameterProcessRegulation.setProcessClassify(RegulationProcessClassify.InputParameterProcessExecutor);
			inputParameterProcessRegulation.setValue(RegulationProcessClassify.InputParameterProcessExecutor.name());
			ApiRegulation inputParameterProcessApiRegulation=ApiRegulation.builder().regulation(inputParameterProcessRegulation).pos(Integer.MIN_VALUE).build();
			apiRegulations.add(inputParameterProcessApiRegulation);
			
			
			
			Regulation outputCacheAccessProcessRegulation=new Regulation();
			outputCacheAccessProcessRegulation.setProcessClassify(RegulationProcessClassify.OutputCacheReadAccessProcessExecutor);
			outputCacheAccessProcessRegulation.setValue(RegulationProcessClassify.OutputCacheReadAccessProcessExecutor.name());
			ApiRegulation outputCacheAccessProcessApiRegulation=ApiRegulation.builder().regulation(outputCacheAccessProcessRegulation).pos(Integer.MIN_VALUE+1).build();
			apiRegulations.add(outputCacheAccessProcessApiRegulation);
		}
		if (getApiFunctionType()==ApiFunctionType.processExecutor) {
			Regulation outputParameterProcessRegulation=new Regulation();
			outputParameterProcessRegulation.setProcessClassify(RegulationProcessClassify.OutputParameterProcessExecutor);
			outputParameterProcessRegulation.setValue(RegulationProcessClassify.OutputParameterProcessExecutor.name());
			ApiRegulation outputParameterProcessApiRegulation=ApiRegulation.builder().regulation(outputParameterProcessRegulation).pos(Integer.MAX_VALUE-1).build();
			apiRegulations.add(outputParameterProcessApiRegulation);
			
			Regulation outputCacheWriteAccessProcessRegulation=new Regulation();
			outputCacheWriteAccessProcessRegulation.setProcessClassify(RegulationProcessClassify.OutputCacheWriteAccessProcessExecutor);
			outputCacheWriteAccessProcessRegulation.setValue(RegulationProcessClassify.OutputCacheWriteAccessProcessExecutor.name());
			ApiRegulation outputCacheWriteAccessApiRegulation=ApiRegulation.builder().regulation(outputCacheWriteAccessProcessRegulation).pos(Integer.MAX_VALUE).build();
			apiRegulations.add(outputCacheWriteAccessApiRegulation);
			
			apiRegulations.forEach(r->{
				if (r.getRegulation() instanceof DataAccessRegulation) {
					DataAccessRegulation dr=(DataAccessRegulation)r.getRegulation();
					if (dr.getDataType()==DataType.pagelist) {
						getOutput().setPage(true);
					}
				}
			});
		}
		if (ObjectUtil.isEmpty(requestMethod)) {
			requestMethod="GET,POST";
		}
		if (input!=null) {
			input.init();
		}
		if (output!=null) {
			output.initDefaultDs();
		}
		cacheRegionKey = "api." +getKey();
		List<InputParameter> inputParameters = getInput().getInputParameters();
		for (InputParameter inputParameter : inputParameters) {
			if (inputParameter.isUnique()) {
				cacheRegionKey = cacheRegionKey + ":" + "#{" + inputParameter.getKey() + "}";
			}
		}
		if (ObjectUtil.isEmpty(path)) {
			path = "/" + getModule().getSystem().getKey() + "/" + getModule().getAliasKey()+ "/"+key;
		}
		if (apiRegulations!=null) {
			Collections.sort(apiRegulations, new Comparator<ApiRegulation>() {

				@Override
				public int compare(ApiRegulation o1, ApiRegulation o2) {
					return o2.getPos() <o1.getPos()?1:-1;
				}
			});
			int i=0;
			for (ApiRegulation apiRegulation : apiRegulations) {
				apiRegulation.setPos(i);
				i++;
			}
			apiRegulations.forEach(ar->{
				ar.getRegulation().setPos(ar.getPos());
				if (ObjectUtil.isEmpty(ar.getRegulation().getThreadExecutorId())) {
					String key=module.getSystem().getKey()+"."+module.getAliasKey()+".default";
					ar.getRegulation().setThreadExecutorId(key);
				}
				if (apiFunctionType==ApiFunctionType.combination) {
					ar.getRegulation().setExecuteMode(ar.getRegulation().getProcessClassify().executeMode());
				}
			});
		}
		if (ObjectUtil.isEmpty(appKey)) {
			appKey=getModule().getSystem().getKey();
		}
	}
	
	
	
	
	public Api cloneApi() {
		try {
			// 将该对象序列化成流,因为写在流里的是对象的一个拷贝，而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(this);
			// 将流序列化成对象
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(bais);
			Api api = (Api) ois.readObject();
			return api;
		} catch (Throwable e) {
			throw new MFTException(e);
		}
	}
	
	
}
